package com.clei.utils;

import org.apache.http.HttpEntity;
import org.apache.http.HttpMessage;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.StatusLine;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * apache http 工具类
 *
 * @author Y
 * @date 2021-12-16
 */
public final class ApacheHttpUtil {

    /**
     * 空json字符串
     */
    private static final String EMPTY_JSON = "{}";

    /**
     * 默认字符集 UTF-8
     */
    private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;

    /**
     * 问号
     */
    private static final char QUESTION_SIGN = '?';

    /**
     * 等号
     */
    private static final char EQUALS_SIGN = '=';

    /**
     * &
     */
    private static final char AND_SIGN = '&';

    /**
     * 超时时间 默认10秒
     */
    private static final int TIME_OUT = 10000;

    /**
     * HttpClient
     */
    private static final CloseableHttpClient HTTP_CLIENT = HttpClients.createDefault();

    /**
     * RequestConfig
     */
    private static final RequestConfig REQUEST_CONFIG = RequestConfig.custom()
            .setConnectionRequestTimeout(TIME_OUT)
            .setConnectTimeout(TIME_OUT)
            .setSocketTimeout(TIME_OUT)
            .setRedirectsEnabled(true)
            .build();

    private ApacheHttpUtil() {
    }

    /**
     * Get 请求
     *
     * @param url url
     * @return String
     */
    public static String get(String url) {
        return doGet(url, null, null, null);
    }

    /**
     * Get 请求
     *
     * @param url     url
     * @param charset 指定返回字符串的字符集
     * @return String
     */
    public static String get(String url, Charset charset) {
        return doGet(url, null, null, charset);
    }

    /**
     * Get 请求
     *
     * @param url   url
     * @param field field
     * @param value value
     * @return String
     */
    public static String get(String url, String field, Object value) {
        Map<String, Object> params = new HashMap<>(1);
        params.put(field, value);
        return doGet(url, null, params, null);
    }

    /**
     * Get 请求
     *
     * @param url    url
     * @param params params
     * @return String
     */
    public static String get(String url, Map<String, Object> params) {
        return doGet(url, null, params, null);
    }

    /**
     * Get 请求
     *
     * @param url     url
     * @param headers headers
     * @param params  params
     * @param charset charset
     * @return String
     */
    public static String get(String url, Map<String, String> headers, Map<String, Object> params, Charset charset) {
        return doGet(url, headers, params, charset);
    }

    /**
     * Get 请求
     *
     * @param url     url
     * @param headers headers
     * @param params  params
     * @param charset 指定返回字符串使用的字符集
     * @return String
     */
    private static String doGet(String url, Map<String, String> headers, Map<String, Object> params, Charset charset) {
        url = getUrl(url, params);
        HttpGet request = new HttpGet(url);
        request.setConfig(REQUEST_CONFIG);
        addHeaders(request, headers);
        return request(HTTP_CLIENT, request, charset);
    }

    /**
     * post json
     *
     * @param url     url
     * @param entity  entity
     * @param headers headers
     * @return String
     */
    public static String postJson(String url, Object entity, Map<String, String> headers) {
        HttpPost request = new HttpPost(url);
        request.setConfig(REQUEST_CONFIG);
        request.setEntity(getStringEntity(entity));
        addHeaders(request, headers);
        return request(HTTP_CLIENT, request, null);
    }

    /**
     * post json
     *
     * @param url     url
     * @param entity  entity
     * @param headers headers
     * @return String
     */
    public static String postForm(String url, Map<String, Object> entity, Map<String, String> headers) {
        HttpPost request = new HttpPost(url);
        request.setConfig(REQUEST_CONFIG);
        request.setEntity(getFormEntity(entity));
        addHeaders(request, headers);
        return request(HTTP_CLIENT, request, null);
    }

    /**
     * 请求
     *
     * @param httpClient client
     * @param request    request
     * @param charset    指定返回字符串使用的字符集
     * @return String
     */
    public static String request(CloseableHttpClient httpClient, HttpUriRequest request, Charset charset) {
        String res = null;
        try (CloseableHttpResponse response = httpClient.execute(request)) {
            if (null == charset) {
                charset = DEFAULT_CHARSET;
            }
            // res
            String result = EntityUtils.toString(response.getEntity(), charset);
            StatusLine statusLine = response.getStatusLine();
            if (HttpStatus.SC_OK != statusLine.getStatusCode()) {
                PrintUtil.log("http 响应状态错误 uri : {}, headers : {}, statusLine : {}, result : {}",
                        request.getURI(), request.getAllHeaders(), statusLine, result);
            } else {
                res = result;
            }
        } catch (Exception e) {
            PrintUtil.log("请求出错 uri : {}, headers : {}", request.getURI(), request.getAllHeaders());
        }
        return res;
    }

    /**
     * get url
     *
     * @param url    url
     * @param params params
     * @return String
     */
    private static String getUrl(String url, Map<String, Object> params) {
        String paramString = getUrlParamString(params);
        return null == paramString ? url : url + QUESTION_SIGN + paramString;
    }

    /**
     * url拼接字符串
     *
     * @param params params
     * @return String
     */
    private static String getUrlParamString(Map<String, Object> params) {
        if (CollectionUtil.isEmpty(params)) {
            return null;
        }
        StringBuilder sb = new StringBuilder();
        int size = params.size();
        int i = 0;
        try {
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                sb.append(entry.getKey());
                sb.append(EQUALS_SIGN);
                Object v = entry.getValue();
                sb.append(URLEncoder.encode(null == v ? "" : String.valueOf(v), DEFAULT_CHARSET.name()));
                i++;
                if (i < size) {
                    sb.append(AND_SIGN);
                }
            }
        } catch (Exception e) {
            PrintUtil.log("getUrlParamString error ", e);
        }
        return sb.toString();
    }

    /**
     * 添加请求头
     *
     * @param message message
     * @param headers headers
     */
    private static void addHeaders(HttpMessage message, Map<String, String> headers) {
        if (!CollectionUtil.isEmpty(headers)) {
            headers.forEach(message::addHeader);
        }
    }

    /**
     * formEntity
     *
     * @param entity entity
     * @return HttpEntity
     */
    private static HttpEntity getFormEntity(Map<String, Object> entity) {
        List<NameValuePair> parameters;
        if (CollectionUtil.isEmpty(entity)) {
            parameters = Collections.emptyList();
        } else {
            parameters = entity.entrySet().stream()
                    .map(entry -> new BasicNameValuePair(entry.getKey(), String.valueOf(entry.getValue())))
                    .collect(Collectors.toList());
        }
        return new UrlEncodedFormEntity(parameters, DEFAULT_CHARSET);
    }

    /**
     * jsonEntity
     *
     * @param entity entity
     * @return HttpEntity
     */
    private static HttpEntity getStringEntity(Object entity) {
        String json = JsonUtil.toJson(entity);
        if (null == json) {
            json = EMPTY_JSON;
        }
        return new StringEntity(json, ContentType.APPLICATION_JSON);
    }
}